(function(global){
    ujs.define('str', 'ujs', {            
        /**
         * 格式化字符串.
         * eg:'aaaaa{0}bbbbb{1}cccc{2}'.format(100,200,300) 
         */
        format : function(str) {
            if (arguments.length === 1){
                var isFormatted = false;            
                //'Date is {Time:1375286400000}'.format()
                str = str.replace( /{([^}]+)}/g, function(matchedStr, subMatchedStr1){            
                    var expressionMembers = subMatchedStr1.split(':'),
                        converterName = expressionMembers[0],
                        rawValue = expressionMembers[1];
                    if(converterName && rawValue){
                        isFormatted = true;                
                        var converter = String.prototype['to' + converterName.trim().capitalize()];
                        if(converter){
                            return converter.call(rawValue.trim().replace(/^\"|\"$/gi,'').replace(/^\'|\'$/gi,''));
                        } else {
                            return rawValue;
                        }
                    }
                });
                return isFormatted ? str : str;    
            }
            if(ujs.Type.isObject(arguments[1])){
                return this.formatByMap(arguments[0], arguments[1]);
            } else {
                for (var s = str, i = 1, len = arguments.length; i < len; i++) {
                    s = s.replace(new RegExp("\\{" + i + "\\}", "g"), arguments[i]);
                }
                return s;
            }
        },
        /**
         * eg: 'aaaaa{key1}bbbbb{key2|key4}cccc{key3.item}'.format({key1:100, key2:200, key3: { item:300 }, key4 : 400 })
         */
        formatByMap : function(str, mapObj) {
            if (mapObj) {        
                str = str.replace( /{([^}]+)}/g, function(matchedStr, subMatchedStr){
                    var subMatchedStrArr = subMatchedStr.split('|'),
                        expressionValue;
                    for(var i=0; i<subMatchedStrArr.length; i++){
                        var subMatchedStr1 = subMatchedStrArr[i];
                        var expressionMembers = subMatchedStr1.split('.');
                        expressionValue = mapObj;
                        
                        do{
                            expressionValue = expressionValue[expressionMembers.shift()];    
                        } while( expressionValue && expressionMembers.length );
                        
                        if(!ujs.Type.isUndefined(expressionValue)){
                            break;
                        }
                    }               
                    return ujs.Type.isUndefined(expressionValue) ? '' : expressionValue;
                });
            }
            return str;
        },
        /**
         * 判断字符串是否在指定的集合中.
         */
        'in' : function(str, collection){
            for(var i=0,len=collection.length; i<len; i++){
                if(str == collection[i]){
                    return true;
                }
            }
            return false;
        },
        /**
         * 反转字符串.
         */
        reverse : function(str){
            return str.split('').reverse().join('');
        },
        /**
         * @param prefix {String|Regexp}
         */
        startsWith : function(str, prefix) {
            var pos = str.indexOf(prefix);
            return pos === 0 || (pos && pos.index == 0);
        },
        /**
         * 
         */
        endsWith : function(str, suffix) {
            return str.indexOf(suffix, str.length - suffix.length) !== -1;
        },
        /**
         * 获取扩展名.
         */
        getExtension : function(str) {
            var strParts = str.split('.');
            if (strParts.length > 1) {
                return strParts[strParts.length - 1];
            } else {
                return "";
            }
        },
        removeExtension : function(str){
            return str.split('.')[0];
        },
        /**
         * 去掉两边的空格, 或去掉字符串两边指定的正则匹配.
         */
        trim : function(str, regExp) {    
            if(ujs.Type.isString(regExp)){
                return str.replace(new RegExp('^(' + regExp + ')|(' + regExp + ')$', 'gi'), '');
            } else if(ujs.Type.isRegExp(regExp)){
                return str.replace(new RegExp('^(' + regExp.source + ')|(' + regExp.source + ')$', 'gi'), '');
            } else {
                return str.replace(/^[\s　]+|[\s　]+$/g, '');
            }
        },
        getMatches : function(str, regExp, isGetDetails){
            return regExp.getMatches(str, isGetDetails);
        },
        /**
         * 获取字符中尾部的数字.
         */
        getSuffixNumber : function(str){
            var matches = /[0-9.]+$/.getMatches(str.trim());
            return matches && matches.length ? parseFloat(matches[0]) : null;      
        },
        /**
         * Return a new string without leading whitespace
         */
        trimLeft : function(str) {
            return str.replace(/^\s+/,"");
        },
        /**
         * Return a new string without trailing whitespace
         */
        trimRight : function(str) {
            return str.replace(/\s+$/,"");
        },
        /**
         * 去掉字符串两边的空标记.
         */
        trimEmptyTags : function(str){
            return str.trim().trim(/(<(?!\/)[^>]+>)+(<\/[^>]+>)+/);    
        },
        /**
         * 使首字母大写，其它字母小写.
         */
        capitalize : function(str) {
            return str.toLowerCase().replace(/(^\w{1})/gi, function($1) {
                return $1.toUpperCase();
            });
        },
        /**
         * 使首字母小写，其它字母不变.
         */
        lowercaseFirstChar : function(str) {
            return str.replace(/(^\w{1})/gi, function($1) {
                return $1.toLowerCase();
            });
        },
        /**
         * 使首字母大写，其它字母不变.
         */
        uppercaseFirstChar : function(str) {
            return str.replace(/(^\w{1})/gi, function($1) {
                return $1.toUpperCase();
            });
        },
        /**
         * 去掉字符串中的脚本标签。
         */
        stripScript : function(str){
            return str.replace(/<script[^>]*>([\s\S]*?)<\/script>/mgi, '');
        },
        /**
         * 将字符串中的url地址转换成链接。
         */
        httpHtml : function(str){
            var regExp = /(http:\/\/|https:\/\/|www\.)((\w|=|\?|\;|\#|\~|\^|\$|\%|\*|\!|\:|\@|\(|\)|\.|\/|&|-)+)/gi;
            //return str.replace(regExp, '<a href="$1$2" target="_blank">$1$2</a>');
            return str.replace(regExp, function(url, $1, $2){
                if($1.toLowerCase() == 'www.'){
                    $1 = 'http://www.';
                }
                url = $1 + $2;
                return '<a href="{0}" target="_blank">{0}</a>'.format(url);
            });
        },
        /**
         * 
         */
        safeString : function(str) {
            var escape = {
                "&" : "&amp;",
                "<" : "&lt;",
                ">" : "&gt;",
                '"' : "&quot;",
                "'" : "&#x27;",
                "`" : "&#x60;"
            };
            return str.replace(/[&<>"'`]/g, function(chr) {
                return escape[chr] || "&amp;";
            });
        },
        /**
         * 
         */
        unsafeString : function(str) {
            var _this = str,
                unescape = {    
                    '&lt;' : '<',
                    '&gt;' : '>',
                    '&quot;' : '"',
                    '&#x27;' : "'",
                    '&#x60;' : '`'            
                };
                        
            Object.each(unescape, function(key, value){
                _this = _this.replace(new RegExp(key, 'gi'), value);
            });
            return _this.replace(new RegExp('&amp;', 'gi'), '&');
        },
        /**
         * 将字符串中的Html标记去掉.
         * @param isIgnoreLinebreak {Boolean} 是否忽略换行符.
         */
        toPlainText : function(str, isIgnoreLinebreak) {
            if(isIgnoreLinebreak === true){
                var tmp = document.createElement("DIV");
                tmp.innerHTML = str;
                return tmp.textContent || tmp.innerText || '';    
            } else {
                return str.replace(
                    /<(\w+)>(((?!<\/\1>).)*)<\/\1>/mgi, //提取标记内容.
                    function(m,p1,p2){
                        return '\n' + p2 + '\n';
                    }
                )
                .replace(/(\n+)|<br>|<br\/>/gi,'\n')    //转换<br/>
                .replace(/\&nbsp;/gi, ' ')              //转换&nbsp;
                .replace(/(<(?!\/)[^>]+>)+(<\/[^>]+>)+/mgi, '') //去掉空标记.
                .replace(/<\/?[^>]+>/mgi, '');  //去掉其它标记头或尾.
            }
        },
        /**
         * Convert HTML breaks to newline
         */
        br2nl : function(str) {
            return str.replace(/<br\s*\/?>/mg,"\n");
        },
        /**
         * Convert newline to HTML breaks
         */
        nl2br : function(str) {
            return str.replace(/\n/g, "<br/>");
        },
        /**
         * Convert HTML whitespace to normal whitespace
         */
        nbsp2s : function(str) {
            return str.replace(/\&nbsp;/mg," ");
        },
        /**
         * 网页中高亮显示字符串中的关键词.
         * 关键词以 #em# 开始，以 $em$ 结束。
         * 如将：Json path "#em#code$em$" is operation  Ids Response: Json path "#em#code$em$
         * 转化成：Json path "<b style="color:#fc4444">code</b>" is operation  Ids Response: Json path "<b style="color:#fc4444">code</b>
         * 
         * 使用场景：在使用 lucene 做搜索时，若需要支持使用 lucene 语法模糊查询，则纯前端难以完成关键词高亮显示功能，需要由server端完成高亮关键词指定，由前端显示。
         * @method highlight
         * @param keys {Array}
         * @return {String}
         */
        highlight : function(str, keys){
            var result = str,
                wrapperHtml = '<b style="color:#fc4444">{0}</b>';
                
            if(ujs.Type.isArray(keys)){
                for(var i=0; i<keys.length; i++){
                    result = result.replace(new RegExp(keys[i], 'igm'), function(match){
                        return wrapperHtml.format(match);
                    });
                }
            } else {
                var key = keys + '';
                return this.highlight([ key ]);
            }    
            return result.replace(/#em#(.+?)\$em\$/gi, wrapperHtml.format('$1'));
        },
        /**
         * 将url地址补全为合法地址.
         * @param {String} : defProtocol 默认补全的协议名.
         */
        toLegalURL : function(str, defProtocol) {
            if(str.startsWith(/((\w)+:\/\/)|\/\//)){
                //以 word:// 或 //开头时.
                return str;
            }
            defProtocol = defProtocol || 'http';     
            var protocolPrefixs = ['http://', 'https://', 'ftp://', 'telnet://', 'pop://'];
            for(var i=0; i<protocolPrefixs.length; i++){
                if(str.toLowerCase().startsWith(protocolPrefixs[i])){
                    return str;
                }
            }
            return defProtocol + '://' + str;
        },
        /**
         * 去掉url前缀
         * TODO 使支持https,ftp等其它协议，使用正则表达式匹配算法，性能优化.
         */
        removeUrlPrefix : function(url){
            url = url.replace(/：/g,":").replace(/．/g,".").replace(/／/g,"/").trim().toLowerCase();
            while(url.indexOf("http://") === 0){
                url = url.replace(/http:\/\//i,"");
            }
            return url;
        },
        /**
         * 计算字符串的长度.
         */
        measureText : (function(){
            var ctx = document.createElement("canvas").getContext("2d");    
            return function(str, font){                
                if(font){
                    ctx.font = font;
                }
                return ctx.measureText(str).width;        
            };
        })(),    
        /**
         * Count the number of times a substring is in a string.
         */
        substrCount : function(str, s) {
            return str.length && s ? (str.split(s)).length - 1 : 0;
        },
        /**
         * 字符串长度截取
         */
        cut : function(str, len, postfix) {
            if (typeof postfix === 'undefined') {
                postfix = '...';
            }
            var patrn = /[^\x00-\xff]/,
                chr, strCount = 0,
                result = [],
                i = 0,
                strLen = str.length;
            for (; i < strLen && strCount < len; i++) {
                chr = str.substr(i, 1);
                patrn.exec(chr) ? strCount += 2 : strCount++;
                if (strCount > len) break;
                result.push(chr);
            }
            result = result.join('');
            return result + (result.length === strLen ? '': postfix);
        },
        /**
         * 使indexOf支持Regex查找.
         */
        indexOf : (function(){
            var originalIndexOf = String.prototype.indexOf;
            return function(str, searchValue, fromIndex ){
                if(typeof searchValue === 'number'){
                    searchValue = searchValue + '';
                }                
                if(typeof searchValue === 'string'){
                    return originalIndexOf.call(str, searchValue, fromIndex);
                }                
                if(searchValue instanceof RegExp){
                    if( fromIndex === undefined ){
                        fromIndex = 0;
                    }                    
                    var searchStr = str.substr(fromIndex),
                        match = searchValue.exec(searchStr);                    
                    return ( !match ) ? -1 : {
                        str : match[0],
                        index : fromIndex + match['index']   
                    };
                }                
                throw 'Invalid Search Value, from: String.prototype.indexOf';
            }; 
        })(),    
        /**
         * 截取字符串并显示省略号.
         */
        ellipse : function(str, len, delimiter){
            if(!len || str.length <= len){
                return str;
            }
            delimiter = delimiter || '...';    
            return str.substr(0,len) + delimiter;    
        },
        /**
         * 判断当前字符串中是否包含指定的匹配.
         * @param {RegExp} / {String} regExp
         * @return {Boolean}.
         */
        contains : function(str, regExp /*or str*/, sep, isCaseSensitive){
            var _this = str;
            if(isCaseSensitive !== true && ujs.Type.isString(regExp)){
                regExp = regExp.toLowerCase();
                _this = _this.toLowerCase();        
            }
            if(!sep){
                //search() 方法不执行全局匹配，它将忽略标志 g。
                //它同时忽略 regexp 的 lastIndex 属性，并且总是从字符串的开始进行检索，这意味着它总是返回 stringObject 的第一个匹配的位置。     
                return _this.search(regExp /*or str*/) > -1;
            } else {
                if(ujs.Type.isRegExp(regExp)){
                    throw '使用分隔符时不支持正则表达式搜索.';
                }
                var str = regExp;
                return (sep + _this + sep).indexOf(sep + str + sep) > -1;
            }
        },
        /**
         * 清理字符串,将字符串中的连续空字符合并为一个空格.
         */
        clean : function(str){
            return String(str).replace(/\s+/g, ' ').trim();
        },
        /**
         * 获取当前字符串中的所有模式匹配. 
         * @param {RegExp} regExp
         * @param {Boolean} isGetDetails    : 仅返回匹配字符串，还是每次匹配的详细信息，默认仅返回匹配字符串。
         * 
         * @return {Array:Object} / {Array:String}
         */
        getMatches : function(str, regExp, isGetDetails){
            if(regExp && regExp instanceof RegExp){
                return regExp.getMatches(str, isGetDetails);
            }
            throw 'Require RegExp object parameter, from string.prototype.getMatches.';
        },
        toInt : function(str, base){
            return parseInt(str, base || 10);
        },
        toFloat : function(str){
            return parseFloat(str) / ( str.endsWith('%') ? 100 : 1 );
        },
        toBool : function(str){
            return str.indexOf('true') === 0;
        },
        toTime : function(str){
            return Date.from(parseInt(str)).format(window.ujsConfig ? window.ujsConfig.dateTimeFormat : "dd-MN-yyyy h:m");     
        },
        toJSON : function(str){
            return JSON.parse(str);
        },
        /**
         * 获取字符串的后缀数字，如：pageContainer59，中，获取59.
         */
        suffixNumber : function(str){
            return parseFloat( str.trim().replace(/^[^\d]+/, '') ) || 0;
        },       
        
        test : function(str, regex, params){
            regex = ujs.Type.isRegExp(regex) == 'regexp' ? regex : new RegExp('' + regex, params);
            return regex.test(str);
        },
        /**
         * 将字符串当作成员调用表达式求值。
         * eg : 'ujsConfig.version'.evaluate().
         * @param {Object} ctx : 默认window.
         */
        evaluate : function(str, ctx){
            var self = str.trim();
            if(String.isExpression(self)){
                if(self.indexOf('@') === 0){
                    var value = self.substring(1);
                    if(value.indexOf('@') === 0){
                        value = eval('(' + value.substring(1) + ')');
                    }    
                } else {
                    var value = ctx || window,
                        membersChain = str.trim().split('.');
                    while(membersChain.length && value){
                        value = value[membersChain.shift()];
                    }  
                }
                return value;
            } else {
                return self;
            }
        },
        /**
         * 解析字符串中HTML Entity表示的字符.
         */
        parseHTMLEntity : function(str){
            var div = document.createElement('div');
            div.innerHTML = str;
            return div.innerHTML;    
        },
        /**
         * 获取字符串最后的文件名，如在字符串http://acucom.acucom.org/ui/index.html中获取index.html。
         */
        getFileName : function(str){
            return str.split(/\/|\\/gi).last();
        },
        /**
         * 去掉字符串中所有的空格.
         */
        shrink : function(str){
            return str.replace(/\s+/g,'');  
        },
        
        /**
         * 生成唯一ID.
         */
        newGuid : (function() {        
            var prevGenTime = null,
                prevGuidIndex = 0;
            /**
             * @prefix : {String} : 生成的Guid的前缀，如：pre_1379914908358_0。
             * @radius : {Number} : 为了缩短guid的长度，将guid转换成数字后，再转换成指定进制(2-36)的数值。
             * 
             * @return : {String} : GUID。
             */
            return function(prefix/* or radius */) {
                var currentGenTime = Date.tick();
                if( prevGenTime !== currentGenTime ){
                    prevGenTime = currentGenTime;
                    currentGuid = 0;
                }    
                if(ujs.Type.isNumber(prefix)){
                    var radius = prefix.constraintRegion(2, 36).toInt();            
                    return parseInt(prevGenTime + '' + ( currentGuid++ )).toString(radius);
                } else {
                    return (prefix || '' ) + prevGenTime + '_' + ( currentGuid++ );
                }
            };
        })(),
        /**
         * 第2种生成唯一ID的算法.
         */
        newGuid2 : function(){
            return Number(new Date()).toString() + 
                parseInt(10 * Math.random()) + 
                parseInt(10 * Math.random()) + 
                parseInt(10 * Math.random());
        },         
        /**
         * character enumeration
         */
        forEach : function(str, fun, ctx) {            
            ujs.arr.forEach(str.split(""), function(chr, index) {
                fun.call(ctx, chr, index, str);
            });
        },              
        /**
         * 获取指定长度的随机字母表字符组成的字符串.
         * @param {Number} len
         */
        getRandomAlphabetStr : function (len) {       
            for(var rdmStr = ""; rdmStr.length < len; rdmStr += Math.random().toString(36).substr(2));  
            return rdmStr.substr(0, len);    
        },        
        /**
         * 封装消息文本；若消息为空，则显示水平垂直居中的默认提示.
         * @param {String} msg
         * @param {String} defaultMsg
         */
        wrapMsg : function(msg, defaultMsg){
            if(!msg){
                return '<div style="height: 100%;letter-spacing: 1px;" class="hvBox">' + defaultMsg + '</div>';
            } else {
                return '<div>{0}</div>'.format(msg);
            }      
        },
        /**
         * 获取Url地址中的文件名称.
         */
        getFileNameOfUrl : function(url){
            if(url){
                url = url.split('?')[0];
                return window.decodeURIComponent(url.split('/').last());
            }
        },
        getFileTypeIcon : function(fileName){
            if(fileName){
                var extentionName = fileName.getExtension() || 'txt';
                return '<span class="icon16 fileType fileType-{0}"></span>'.format( extentionName.toLowerCase() );
            }
        },
        /**
         * 转换成全角字符串.
         */
        toSBC : function(str){
            var result = [];
            if(str){
                for(var i = 0; i < str.length; i++) {
                    var charCode = str.charCodeAt(i);
                    if(charCode >= 33 && charCode <= 126) {
                        result.push(String.fromCharCode(str.charCodeAt(i) + 65248));
                    } else if (charCode == 32) {
                        result.push(String.fromCharCode(str.charCodeAt(i) + 12288 - 32));
                    } else {
                        result.push(str.charAt(i));
                    }
                }
            }
            return result.join('');
        },
        /**
         * 转换成半角字符串.
         */
        toDBC : function(str){
            var result = [];
            if(str){
                for(var i=0; i < str.length; i++){
                    var charCode = str.charCodeAt(i);
                    if(charCode >= 65281 && charCode <= 65374){
                        result.push(String.fromCharCode(str.charCodeAt(i) - 65248));
                    }else if (charCode == 12288){
                        result.push(String.fromCharCode(str.charCodeAt(i) - 12288 + 32));
                    }else{
                        result.push(str.charAt(i));
                    }
                }
            }
            return result.join('');
        },        
        /**
         * 将字符串作为JavaScript代码执行.
         * @return {Boolean} 代码是否正确执行.
         */
        execJS : function(str){  
            if(str.length){
                if (window.execScript){
                    window.execScript(str);
                } else {
                    var script = document.createElement('script'),
                        head = document.head;
                    script.setAttribute('type', 'text/javascript');
                    script.text = str;
                    head.appendChild(script);
                    //JS代码执行完毕后，可立即删除script节点.
                    head.removeChild(script);
                    return true;
                }
            } else {
                return false;
            }  
        },
        /**
         * 将字符串作为CSS代码执行.
         * 取消CSS代码效果时，根据返回的styleID，删除style节点即可。
         * @return {String|Boolean} 若执行成功，则返回样式表ID，否则返回false。
         */
        execCSS : function(str){  
            if(str.length){        
                var style = document.createElement('style'),
                    head = document.head,
                    styleID = String.newGuid();
                style.setAttribute('id', styleID);
                style.setAttribute('type', 'text/css');
                style.innerText = str;
                head.appendChild(style);
                return styleID;        
            } else {
                return false;
            }  
        },
        IPToInt : function(ip){
            ip = ip.split(".");
            var num = 
                Number(ip[0]) * 256 * 256 * 256 + 
                Number(ip[1]) * 256 * 256 + 
                Number(ip[2]) * 256 + 
                Number(ip[3]);
            return num >>> 0;
        },   
        IPFromInt : function(num) {
            var ip = [];
            ip[0] = (num >>> 24) >>> 0;
            ip[1] = ((num << 8) >>> 24) >>> 0;
            ip[2] = (num << 16) >>> 24;
            ip[3] = (num << 24) >>> 24;
            return ip.join('.');
        },
        utf8_encode : function(str){
            str = str.replace(/\r\n/g,"\n");
            var utftext = "";
            for (var n = 0; n < str.length; n++) {
                var c = str.charCodeAt(n);
                if (c < 128) {
                    utftext += String.fromCharCode(c);
                } else if((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                } else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                } 
            }
            return utftext; 
        },
        utf8_decode : function(str){
            var tmp_arr = [],
                i = 0, ac = 0, c1 = 0, c2 = 0, c3 = 0;
            str += '';
            while (i < str.length) {
                c1 = str.charCodeAt(i);
                if (c1 < 128) {
                    tmp_arr[ac++] = String.fromCharCode(c1);
                    i++;
                } else if (c1 > 191 && c1 < 224) {      
                    c2 = str.charCodeAt(i + 1);
                    tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
                    i += 2;
                } else {
                    c2 = str.charCodeAt(i + 1);
                    c3 = str.charCodeAt(i + 2);
                    tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                    i += 3;
                }
            }
            return tmp_arr.join('');
        }
    });  
    
    //---------------------------------------------------------------------------------------------------------------------------------
    
    ujs.alias(ujs.str, ujs.str['in'], 'belongTo,at');
    
    ujs.alias(ujs.str, ujs.str.newGuid, 'newId,newID,newid,id');
    ujs.alias(ujs, ujs.str.newGuid, 'newId,newID,newid,newGuid');
    
})(window);